/* eslint-disable @typescript-eslint/no-explicit-any */
import { cls } from '@knno/jsx';
import time from '@knno/time';
import { type DateMode, DatePicker, type DatePickHandler } from '../../datePicker/datePicker';
import { DATA } from '../../symbol';
import { cellStyle } from '../cell.css';
import { inlineStyle } from '../inline.css';
import { InputBase, type InputBaseProps } from '../inputBase';
import { dateInputStyle } from './date.css';

export type DateInputProps<T extends DateInputBase<any, any, any>> = InputBaseProps<T, string> & {
	min?: string;
	max?: string;
	mode?: DateMode;
	/**
	 * format date string, for display only, e.g.: yyyy-MM-dd
	 */
	format?: string;
};

abstract class DateInputBase<
	P extends DateInputProps<C>,
	C extends DateInputBase<P, C, E>,
	E extends HTMLElement,
> extends InputBase<P, C, E, string> {
	[DATA]?: DatePickHandler;
	constructor(props: P) {
		super(props);
		this.setMin(this.min);
		this.setMax(this.max);
		this.setMode(this.mode);
		this.setFormat(this.format);
		this.el.tabIndex = 0;
		this.el.classList.add(dateInputStyle);
		this.el.addEventListener('click', () => {
			if (this.readOnly) return;
			this.openSelect();
		});
		this.el.addEventListener('keypress', (evt) => {
			if (this.readOnly) return;
			if (evt.key === ' ' || evt.key === 'Space' || evt.key === 'Enter') {
				evt.preventDefault();
				evt.stopPropagation();
				if (!this.readOnly) {
					this.openSelect();
				}
			}
		});
	}
	private openSelect() {
		if (this[DATA] !== undefined) return;
		const date = this.value !== undefined ? DatePicker.parseDate(this.value, this.mode) : undefined;
		this[DATA] = DatePicker.show({
			refer: this.el,
			focusOnSelect: this.el,
			date: date ? [date] : undefined,
			mode: this.mode,
			minDate: this.min ? (DatePicker.parseDate(this.min, this.mode) ?? undefined) : undefined,
			maxDate: this.max ? (DatePicker.parseDate(this.max, this.mode) ?? undefined) : undefined,
		})
			.onSelect((value) => {
				if (value instanceof Array) {
					value = value[0];
				}
				const newValue = value ? DatePicker.formatDate(value, this.mode) : undefined;
				this.el.textContent = this.formatDate(newValue);
				this.updateValue(newValue);
			})
			.onClose(() => {
				delete this[DATA];
			});
	}
	protected override renderContent(): void {
		this.el.textContent = this.formatDate(this.value);
	}
	protected override changeReadOnly(): void {}

	private formatDate(value: string | undefined): string {
		if (value === undefined) {
			return '';
		}
		if (this.format !== undefined) {
			const date = DatePicker.parseDate(value, this.mode);
			if (date === null) {
				return '';
			} else {
				return time.format(date, this.format) ?? '';
			}
		} else {
			return value;
		}
	}
	get mode(): DateMode {
		return this.props.mode ?? 'dd';
	}
	setMode(mode: DateMode): this {
		this.props.mode = mode;
		if (mode.includes('t')) {
			this.el.classList.remove('date');
			this.el.classList.add('time');
		} else {
			this.el.classList.add('date');
			this.el.classList.remove('time');
		}
		this.setMax(this.max);
		return this;
	}
	get format(): string | undefined {
		return this.props.format;
	}
	setFormat(format: string | undefined): this {
		this.props.format = format;
		this.renderContent();
		return this;
	}
	get min(): string | undefined {
		return this.props.min;
	}
	setMin(min: string | undefined): this {
		this.props.min = min;
		return this;
	}
	get max(): string | undefined {
		return this.props.max;
	}
	setMax(max: string | undefined): this {
		this.props.max = max;
		if (max !== undefined && this.value !== undefined) {
			const maxDate = DatePicker.parseDate(max, this.mode);
			const value = DatePicker.parseDate(this.value, this.mode);
			if (maxDate !== null && value !== null && value > maxDate) {
				const newValue = DatePicker.formatDate(maxDate, this.mode);
				this.el.textContent = this.formatDate(newValue);
				this.updateValue(newValue);
			}
		}
		return this;
	}
}

export type DateCellProps = DateInputProps<DateCell> & {
	colSpan?: number;
	rowSpan?: number;
};

export class DateCell extends DateInputBase<DateCellProps, DateCell, HTMLTableCellElement> {
	protected render(): Node {
		return (
			<td
				class={cls(this.props.class, cellStyle)}
				style={this.props.style}
				colSpan={this.colSpan}
				rowSpan={this.rowSpan}
			/>
		);
	}
	get colSpan(): number {
		return this.props.colSpan ?? 1;
	}

	setColSpan(value: number): this {
		this.props.colSpan = value;
		this.el.colSpan = value;
		return this;
	}

	get rowSpan(): number {
		return this.props.rowSpan ?? 1;
	}

	setRowSpan(value: number): this {
		this.props.rowSpan = value;
		this.el.rowSpan = value;
		return this;
	}
}

export class DateInline extends DateInputBase<
	DateInputProps<DateInline>,
	DateInline,
	HTMLSpanElement
> {
	protected render(): Node {
		return <span class={cls(this.props.class, inlineStyle)} style={this.props.style} />;
	}
}
